package com.huiyun.project.im.api.biz.controller;

import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.huiyun.project.base.controller.BaseController;
import com.huiyun.project.common.core.domain.R;
import com.huiyun.project.common.core.domain.req.SimpleCommonReq;
import com.huiyun.project.common.core.util.NumUtils;
import com.huiyun.project.common.core.util.fn.FunctionUtils;
import com.huiyun.project.im.api.client.utils.ImSecureUtil;
import com.huiyun.project.im.api.client.vo.req.AppTokenReq;
import com.huiyun.project.im.api.client.vo.req.LoginReq;
import com.huiyun.project.im.api.client.vo.resp.TokenResp;
import com.huiyun.project.im.common.auth.SecurityUtils;
import com.huiyun.project.im.consts.ImServerConst;
import com.huiyun.project.im.core.service.IImAppService;
import com.huiyun.project.im.core.service.IImUserService;
import com.huiyun.project.im.entity.ImApp;
import com.huiyun.project.im.entity.ImUser;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.wf.jwtp.annotation.Inner;
import org.wf.jwtp.annotation.RequiresRoles;
import org.wf.jwtp.provider.Token;
import org.wf.jwtp.provider.TokenStore;
import org.wf.jwtp.provider.TokenSubject;

import java.util.List;
import java.util.Map;

/**
 * @FileName:ApiImSpController
 * @Description: 提供给服务商的接口
 * @Author linjie
 * @Date 2020/7/1711:19 AM
 * @Version 4.0.0
 */
@RestController
@RequestMapping("/im/api/im/{version}")
@AllArgsConstructor
@Api(tags = "系统认证")
public class ApiImController extends BaseController {


    private final IImAppService appService;


    private final TokenStore tokenStore;


    private final IImUserService userService;


    /**
     * 用于获取REST API操作权限
     *
     * @param tokenReq
     * @return
     */
    @PostMapping("/token")
    @ApiOperation(value = "获取应用Token", notes = "accessToken有效期为2个小时")
    @Inner(value = false)
    public R<TokenResp> token(@RequestBody AppTokenReq tokenReq) {
        return FunctionUtils.wrapper(() -> {
            ImApp imApp = appService.findImApp(tokenReq.getAppKey(), tokenReq.getAppSecret(), tokenReq.getSpId());
            Assert.notNull(imApp, "无效的应用信息");
            //拥有供应商接口的API权限(有效期2小时)
            Token token = tokenStore.createNewToken(new TokenSubject().setUserId(ImServerConst.APP_STORE_PREFIX + imApp.getId()), null, new String[]{ImServerConst.API_SP_ROLE}, 2 * 60 * 60);
            return R.ok(buildToken(token));
        });

    }


    /**
     * 用于获取REST API操作权限
     *
     * @param commonReq
     * @return
     */
    @PostMapping("/refresh")
    @ApiOperation(value = "刷新应用Token")
    @RequiresRoles({ImServerConst.API_SP_ROLE})
    public R<TokenResp> refreshToken(@RequestBody SimpleCommonReq commonReq) {
        return FunctionUtils.wrapper(() -> {
            Token token = tokenStore.refreshToken(commonReq.getParam());
            return R.ok(buildToken(token));
        });
    }


    /**
     * 应用组+用户+密码确认一个用户 ,可用于隐式登陆
     *
     * @return
     */
    @GetMapping("/login")
    @ApiOperation(value = "IM账号登录", notes = "accessToken有效期7天")
    @Inner(value = false)
    public R<TokenResp> login(LoginReq loginReq) {
        return FunctionUtils.wrapper(() -> {
            //1.用appkey查找应用组id
            ImApp app = appService.findByAppKey(loginReq.getAppKey());
            Assert.notNull(app, "无效的应用");
            ImUser user = userService.getOne(Wrappers.query(new ImUser()
                    .setUsername(loginReq.getUsername())
                    .setAppGroupId(app.getAppGroupId())
            ));
            Assert.notNull(user, "用户名或者密码错误");
            boolean pwdResult = ImSecureUtil.getMd5Pwd(loginReq.getPassword(), user.getSalt()).equals(user.getPassword());
            Assert.isTrue(pwdResult, "用户名或者密码错误");
            //校验登录设备类型
            if (!checkLoginUser(user, loginReq)) {
                return R.failed("非法终端登录");
            }
            //拥有供应商接口的API权限(有效期7天)
            String[] roles = {ImServerConst.API_TENANT_ROLE};
            if (NumUtils.ne(user.getType(), 2)) {
                roles = new String[]{ImServerConst.API_USER_ROLE};
            }
            String uid = "";
            if (user.getType().toString().equals("1") || user.getType().toString().equals("2")) {
                uid = ImServerConst.TENANT_STORE_PREFIX + user.getId();
            } else {
                uid = ImServerConst.USER_STORE_PREFIX + user.getId();
            }
            Token token = tokenStore.createNewToken(new TokenSubject().setUserId(uid)
                            .setTerminal(loginReq.getDeviceType())
                            .setAppKey(loginReq.getAppKey())
                            .setAppGroupId(app.getAppGroupId())
                    , null, roles, 24 * 60 * 60 * 7);
            return R.ok(buildToken(token));
        });
    }


    /**
     * 检测IM的token并刷新,一般用于打开应用,并且有accessToken的时候
     *
     * @return
     */
    @GetMapping("/checkLogin")
    @ApiOperation(value = "IM账号Token检测")
    @Inner(value = false)
    public R<TokenResp> checkLogin(@RequestParam("username") String username, @RequestParam("appGroupId") String appGroupId, @RequestParam("accessToken") String accessToken) {
        return FunctionUtils.wrapper(() -> {
            ImUser user = userService.getOne(Wrappers.query(new ImUser()
                    .setUsername(username)
                    .setAppGroupId(appGroupId)
            ));
            Assert.notNull(user, "invalid username {}", username);
            //String userId, String access_toke
            String uid = "";
            if (user.getType().toString().equals("1") || user.getType().toString().equals("2")) {
                uid = ImServerConst.TENANT_STORE_PREFIX + user.getId();
            } else {
                uid = ImServerConst.USER_STORE_PREFIX + user.getId();
            }
            Token token = tokenStore.findToken(uid, accessToken);
            if (token != null) {
                //token = tokenStore.refreshToken(token.getRefreshToken());
                return R.ok(buildToken(token));
            }
            return R.failed();
        });
    }


    /**
     * 刷新token
     *
     * @param commonReq
     * @return
     */
    @PostMapping("/refreshLogin")
    @ApiOperation(value = "刷新登录Token")
    @RequiresRoles({ImServerConst.API_TENANT_ROLE, ImServerConst.API_USER_ROLE})
    public R<TokenResp> refreshLogin(@RequestBody SimpleCommonReq commonReq) {
        return FunctionUtils.wrapper(() -> {
            Token token = tokenStore.refreshToken(commonReq.getParam());
            return R.ok(buildToken(token));
        });
    }


    @GetMapping("/logout")
    @ApiOperation("退出登录")
    public R<Integer> logout() {
        return FunctionUtils.wrapper(() -> {
            Token token = SecurityUtils.getToken();
            return R.ok(tokenStore.removeToken(token.getUserId(), token.getUserId()));
        });
    }

    //校验登录设备类型
    private Boolean checkLoginUser(ImUser user, LoginReq loginReq) {
        if (null == user || null == loginReq || StringUtils.isBlank(loginReq.getDeviceType()) || StringUtils.isBlank(loginReq.getDevicePlatform())) {
            return false;
        }
        List<String> typeList = Lists.newArrayList(ImServerConst.DEVICE_TYPE_1, ImServerConst.DEVICE_TYPE_2, ImServerConst.DEVICE_TYPE_3);
        List<String> platformList = Lists.newArrayList(ImServerConst.DEVICE_PLAT_FORM_0, ImServerConst.DEVICE_PLAT_FORM_1,
                ImServerConst.DEVICE_PLAT_FORM_2, ImServerConst.DEVICE_PLAT_FORM_3, ImServerConst.DEVICE_PLAT_FORM_4);
        if (typeList.contains(loginReq.getDeviceType()) && platformList.contains(loginReq.getDevicePlatform())) {
            return true;
        }
        return false;
    }

    private TokenResp buildToken(Token token) {
        long expire = 0;
        if (null != token.getExpireTime()) {
            expire = (token.getExpireTime().getTime() - System.currentTimeMillis())/1000;
        }
        return new TokenResp().setAccessToken(token.getAccessToken()).setExpireTime(token.getExpireTime())
                .setRefreshToken(token.getRefreshToken()).setRefreshTokenExpireTime(token.getExpireTime()).setExpiresIn(expire);
    }
}
